package com.czk.bootlettuce.servcie.impl;

import com.czk.bootlettuce.servcie.ReadPacketService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.SimpleIdGenerator;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * @author chenzhangkun
 * @ClassName SendReadPacketServiceImpl
 * @description: TODO
 * @date 2025年07月16日
 */
@Service
public class ReadPacketServiceImpl implements ReadPacketService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public String sendRedPacket(Long userId, BigDecimal amount, int count) {
        // 生成红包唯一id
        SimpleIdGenerator simpleIdGenerator = new SimpleIdGenerator();
        String redPacketId = simpleIdGenerator.generateId().toString();
        // 拆分红包
        List<BigDecimal> amounts = splitRedPacket(amount, count);
        // 存储到redis
        // 3. 存储到Redis（原子操作）
        String key = "redpacket:" + redPacketId;
        redisTemplate.opsForList().rightPushAll(key, amounts);
        // 设置过期时间
        redisTemplate.expire(key, 24, TimeUnit.HOURS);
        // 异步同步到数据库
        return redPacketId;
    }

    /**
     * 抢红包核心方法
     *
     * @param redPacketId 红包ID
     * @param userId      用户ID
     * @return 抢到的金额（null表示失败）
     */
    public BigDecimal grabRedPacket(String redPacketId, Long userId) {
        String redpacketKey = "redpacket:" + redPacketId;
        String recordKey = "record:" + redPacketId;

        // 执行Lua脚本（保证原子性）
        DefaultRedisScript<BigDecimal> script = new DefaultRedisScript<>(Script.GRAB_SCRIPT, BigDecimal.class);
        BigDecimal amount = redisTemplate.execute(
                script,
                Arrays.asList(redpacketKey, recordKey),
                userId.toString()
        );

        if (amount != null) {
            // 异步落库（MQ保证最终一致性）
            //sendToMQ(redPacketId, userId, amount);
        }
        return amount;
    }

    // 二倍均值法拆红包（线程安全）
    private List<BigDecimal> splitRedPacket(BigDecimal total, int count) {
        List<BigDecimal> amounts = new ArrayList<>();
        BigDecimal remaining = total;
        for (int i = 0; i < count - 1; i++) {
            // 最大不超过剩余均值的2倍
            BigDecimal max = remaining.divide(BigDecimal.valueOf(count - i), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(2));
            BigDecimal amount = BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble(0.01, max.doubleValue()))
                    .setScale(2, RoundingMode.HALF_UP);
            amounts.add(amount);
            remaining = remaining.subtract(amount);
        }
        amounts.add(remaining);
        return amounts;
    }

    public interface Script {
        String GRAB_SCRIPT = "// Lua脚本（原子化操作）\n" +
                "    private static final String GRAB_SCRIPT = \n" +
                "        \"local redpacketKey = KEYS[1] \" +\n" +
                "        \"local recordKey = KEYS[2] \" +\n" +
                "        \"local userId = ARGV[1] \" +\n" +
                "        \n" +
                "        // 1. 检查是否已抢过\n" +
                "        \"if redis.call('hexists', recordKey, userId) == 1 then \" +\n" +
                "            \"return nil \" +\n" +
                "        \"end \" +\n" +
                "        \n" +
                "        // 2. 从红包列表弹出一个金额\n" +
                "        \"local amount = redis.call('lpop', redpacketKey) \" +\n" +
                "        \"if not amount then \" +\n" +
                "            \"return nil \" +  // 红包已抢完\n" +
                "        \"end \" +\n" +
                "        \n" +
                "        // 3. 记录领取信息\n" +
                "        \"redis.call('hset', recordKey, userId, a\n" +
                "\n";
    }
}
